!! Copyright (C) 2009,2010,2011,2012  Marco Restelli
!!
!! This file is part of:
!!   FEMilaro -- Finite Element Method toolkit
!!
!! FEMilaro is free software; you can redistribute it and/or modify it
!! under the terms of the GNU General Public License as published by
!! the Free Software Foundation; either version 3 of the License, or
!! (at your option) any later version.
!!
!! FEMilaro is distributed in the hope that it will be useful, but
!! WITHOUT ANY WARRANTY; without even the implied warranty of
!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
!! General Public License for more details.
!!
!! You should have received a copy of the GNU General Public License
!! along with FEMilaro; If not, see <http://www.gnu.org/licenses/>.
!!
!! author: Marco Restelli                   <marco.restelli@gmail.com>

!>\brief
!! Linear solvers
!!
!! \n
!!
!! This module defines the common layout for various linear solvers.
!! For additional information, see \c mod_linsolver.
!<----------------------------------------------------------------------
module mod_linsolver_base

!-----------------------------------------------------------------------

 use mod_messages, only: &
   mod_messages_initialized, &
   error,   &
   warning, &
   info

 use mod_state_vars, only: &
   mod_state_vars_initialized, &
   c_stv

!-----------------------------------------------------------------------
 
 implicit none

!-----------------------------------------------------------------------

! Module interface

 public :: &
   mod_linsolver_base_constructor, &
   mod_linsolver_base_destructor,  &
   mod_linsolver_base_initialized, &
   c_linpb

 private

!-----------------------------------------------------------------------

! Module types and parameters

 ! public members

 !> Linear problem
 type, abstract :: c_linpb
 contains 
  procedure(i_factor), deferred, pass(s) :: factor
  procedure(i_solve ), deferred, pass(s) :: solve
  procedure(i_clean ), deferred, pass(s) :: clean
  !> Returns a logical value indicating whether an implementation is
  !! completely functional or simply a dummy module.
  procedure(i_wi), nopass, deferred :: working_implementation
 end type c_linpb

 abstract interface
  subroutine i_factor(s,phase)
   import :: c_linpb
   !> linear problem
   class(c_linpb), intent(inout) :: s
   !> factorization phase
   character(len=*), intent(in), optional :: phase
  end subroutine i_factor
 end interface

 abstract interface
  subroutine i_solve(x,s)
   import :: c_stv, c_linpb
   !> linear problem
   class(c_linpb), intent(inout) :: s
   !> solution
   class(c_stv),   intent(inout) :: x
  end subroutine i_solve
 end interface

 abstract interface
  subroutine i_clean(s)
   import :: c_linpb
   !> linear problem
   class(c_linpb), intent(inout) :: s
  end subroutine i_clean
 end interface

 abstract interface
  pure function i_wi()
   logical :: i_wi
  end function i_wi
 end interface

! Module variables

 logical, protected ::               &
   mod_linsolver_base_initialized = .false.
 character(len=*), parameter :: &
   this_mod_name = 'mod_linsolver_base'

!-----------------------------------------------------------------------

contains

!-----------------------------------------------------------------------

 subroutine mod_linsolver_base_constructor()
  character(len=*), parameter :: &
    this_sub_name = 'constructor'

   !Consistency checks ---------------------------
   if( (mod_messages_initialized.eqv..false.) .or. &
     (mod_state_vars_initialized.eqv..false.) ) then
     call error(this_sub_name,this_mod_name, &
                'Not all the required modules are initialized.')
   endif
   if(mod_linsolver_base_initialized.eqv..true.) then
     call warning(this_sub_name,this_mod_name, &
                  'Module is already initialized.')
   endif
   !----------------------------------------------

   mod_linsolver_base_initialized = .true.
 end subroutine mod_linsolver_base_constructor

!-----------------------------------------------------------------------
 
 subroutine mod_linsolver_base_destructor()
  character(len=*), parameter :: &
    this_sub_name = 'destructor'
   
   !Consistency checks ---------------------------
   if(mod_linsolver_base_initialized.eqv..false.) then
     call error(this_sub_name,this_mod_name, &
                'This module is not initialized.')
   endif
   !----------------------------------------------

   mod_linsolver_base_initialized = .false.
 end subroutine mod_linsolver_base_destructor

!-----------------------------------------------------------------------
 
end module mod_linsolver_base

